package com.xjve.framework.hibernate.jpa;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.List;

/**
 * 搜索执行器，负责执行生成的HQL查询并返回结果
 */
@Slf4j
@Component
public class SearchExecutor {

    @PersistenceContext
    private EntityManager entityManager;

    /**
     * 执行查询，返回结果列表
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> execute(BaseSearch<T> search) {
        SearchExecutor executor = ApplicationContextHolder.getBean(SearchExecutor.class);
        HqlGenerator hqlGenerator = ApplicationContextHolder.getBean(HqlGenerator.class);

        QueryContext queryContext = hqlGenerator.generateQueryContext(search, false);
        TypedQuery<T> query = executor.createQuery(search, queryContext);

        return query.getResultList();
    }

    /**
     * 执行分页查询，返回分页结果
     */
    @SuppressWarnings("unchecked")
    public static <T> Page<T> executePage(BaseSearch<T> search) {
        SearchExecutor executor = ApplicationContextHolder.getBean(SearchExecutor.class);
        HqlGenerator hqlGenerator = ApplicationContextHolder.getBean(HqlGenerator.class);

        // 统计总数
        long total = count(search);

        // 查询分页数据
        QueryContext queryContext = hqlGenerator.generateQueryContext(search, false);
        TypedQuery<T> query = executor.createQuery(search, queryContext);

        // 设置分页参数
        int curPage = search.getCurPage() - 1; // 转换为0-based
        int pageSize = search.getPageSize();
        query.setFirstResult(curPage * pageSize);
        query.setMaxResults(pageSize);

        List<T> content = query.getResultList();

        return new PageImpl<>(content, PageRequest.of(curPage, pageSize), total);
    }

    /**
     * 执行统计查询，返回总数
     */
    public static <T> long count(BaseSearch<T> search) {
        SearchExecutor executor = ApplicationContextHolder.getBean(SearchExecutor.class);
        HqlGenerator hqlGenerator = ApplicationContextHolder.getBean(HqlGenerator.class);

        QueryContext queryContext = hqlGenerator.generateQueryContext(search, true);
        TypedQuery<Long> query = executor.createCountQuery(search, queryContext);

        return query.getSingleResult();
    }

    /**
     * 创建查询对象
     */
    private <T> TypedQuery<T> createQuery(BaseSearch<T> search, QueryContext queryContext) {
        log.info("createQuery hql: {}", queryContext.getHql());
        TypedQuery<T> query = entityManager.createQuery(queryContext.getHql(), search.getEntityClass());
        setParameters(query, queryContext);
        return query;
    }

    /**
     * 创建统计查询对象
     */
    private <T> TypedQuery<Long> createCountQuery(BaseSearch<T> search, QueryContext queryContext) {
        log.info("createCountQuery hql: {}", queryContext.getHql());
        TypedQuery<Long> query = entityManager.createQuery(queryContext.getHql(), Long.class);
        setParameters(query, queryContext);
        return query;
    }

    /**
     * 设置查询参数
     */
    private void setParameters(Query query, QueryContext queryContext) {
        for (SearchFieldMetadata metadata : queryContext.getFieldMetadataList()) {
            String paramName = metadata.getParamName();
            Object paramValue = metadata.getProcessedValue();
            String operator = metadata.getOperator();

            setQueryParameter(query, paramName, paramValue, operator);
        }
    }

    /**
     * 设置查询参数
     */
    private void setQueryParameter(Query query, String paramName, Object value, String operator) {
        if (operator.equals("in") || operator.equals("not in")) {
            // 对于in查询，处理集合参数
            if (value instanceof List) {
                query.setParameter(paramName, (List<?>) value);
            } else if (value.getClass().isArray()) {
                List<Object> list = new ArrayList<>();
                for (Object item : (Object[]) value) {
                    list.add(item);
                }
                query.setParameter(paramName, list);
            } else {
                throw new IllegalArgumentException("In query parameter must be a collection or array");
            }
        } else {
            query.setParameter(paramName, value);
        }
    }
}
